import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
import java.util.Scanner;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.io.*;
import java.nio.file.*;
import java.util.*;

public class WeatherAPIManager implements WeatherAPIService {
    private String baseURL = "https://api.openweathermap.org/data/2.5/";
    private UserPreferences userPreferences;
    private boolean serviceAvailable = true;
    private long lastConnectionTest = 0;
    private static final long CONNECTION_TEST_CACHE_MS = 300000; // 5分钟缓存

    public WeatherAPIManager(UserPreferences userPreferences) {
        this.userPreferences = userPreferences;
        // Force metric units for Celsius temperature
        this.userPreferences.setPreferredUnits("metric");
    }

    @Override
    public String getSourceId() {
        return "openweather";
    }

    @Override
    public String getDisplayName() {
        return "OpenWeatherMap";
    }

    @Override
    public boolean isServiceAvailable() {
        // 缓存连接测试结果，避免频繁检查
        long currentTime = System.currentTimeMillis();
        if (currentTime - lastConnectionTest > CONNECTION_TEST_CACHE_MS) {
            serviceAvailable = testConnection() > 0;
            lastConnectionTest = currentTime;
        }
        return serviceAvailable;
    }
    private void saveWeatherToFile(WeatherData data) {
        File file = new File("weather.json");
        StringBuilder output = new StringBuilder();
        output.append("[\n");

        try {
            List<String> existingJsons = new ArrayList<>();
            if (file.exists()) {
                String existingContent = new String(Files.readAllBytes(file.toPath())).trim();
                if (existingContent.startsWith("[") && existingContent.endsWith("]")) {
                    String content = existingContent.substring(1, existingContent.length() - 1).trim();
                    if (!content.isEmpty()) {
                        String[] items = content.split("\\},\\s*\\{");
                        for (String item : items) {
                            item = item.trim();
                            if (!item.startsWith("{")) item = "{" + item;
                            if (!item.endsWith("}")) item = item + "}";
                            existingJsons.add(item);
                        }
                    }
                }
            }

            // 添加新的数据
            existingJsons.add(data.toJsonString());

            // 重新写入文件
            for (int i = 0; i < existingJsons.size(); i++) {
                output.append(existingJsons.get(i));
                if (i < existingJsons.size() - 1) {
                    output.append(",\n");
                }
            }
            output.append("\n]");

            Files.write(file.toPath(), output.toString().getBytes());

        } catch (IOException e) {
            System.err.println("写入 weather.json 时出错: " + e.getMessage());
        }
    }

    @Override
    public String getBaseURL() {
        return baseURL;
    }

    @Override
    public String getAPILimits() {
        return "1000 calls/day (Free tier), 60 calls/minute";
    }

    @Override
    public boolean isLocationSupported(Location location) {
        // OpenWeatherMap支持全球大部分地区
        return location != null && 
               location.getCity() != null && 
               !location.getCity().trim().isEmpty();
    }

    @Override
    public int getPriority() {
        return 1; // 高优先级（数值越低优先级越高）
    }

    @Override
    public long testConnection() {
        try {
            long startTime = System.currentTimeMillis();
            String testEndpoint = baseURL + "weather?q=London&appid=" + getApiKey() + "&units=metric";
            
            URL url = URI.create(testEndpoint).toURL();
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("HEAD"); // 只检查连接，不获取数据
            conn.setConnectTimeout(5000);
            conn.setReadTimeout(5000);
            
            int responseCode = conn.getResponseCode();
            conn.disconnect();
            
            if (responseCode == 200) {
                long latency = System.currentTimeMillis() - startTime;
                System.out.println("OpenWeatherMap connection test successful, latency: " + latency + "ms");
                return latency;
            } else {
                System.err.println("OpenWeatherMap connection test failed with code: " + responseCode);
                return -1;
            }
            
        } catch (Exception e) {
            System.err.println("OpenWeatherMap connection test error: " + e.getMessage());
            return -1;
        }
    }

    @Override
    public APISourceInfo.APIStatus getAPIStatus() {
        if (!isServiceAvailable()) {
            return APISourceInfo.APIStatus.ERROR;
        }
        
        long latency = testConnection();
        if (latency < 0) {
            return APISourceInfo.APIStatus.ERROR;
        } else if (latency > 3000) {
            return APISourceInfo.APIStatus.ERROR;
        } else {
            return APISourceInfo.APIStatus.ONLINE;
        }
    }

    @Override
    public WeatherData getCurrentWeather(Location location) {
        String response = fetchWeatherData(location, "weather");
        return parseAPIResponse(response);
    }

    @Override
    public WeatherData[] getForecast(Location location, int days) {
        String response = fetchWeatherData(location, "forecast");
        return parseForecastResponse(response, days);
    }

    private String fetchWeatherData(Location location, String endpointType) {
        // 检查位置支持
        if (!isLocationSupported(location)) {
            throw new RuntimeException("Location not supported: " + 
                (location != null ? location.getCity() : "null"));
        }

        // Force metric units to ensure Celsius temperature
        String units = "metric";
        String endpoint = baseURL + endpointType + "?q=" + location.getCity() + "&appid=" + getApiKey() + "&units=" + units;
        
        System.out.println("API Request URL: " + endpoint); // Debug log
        
        StringBuilder inline = new StringBuilder();

        try {
            URL url = URI.create(endpoint).toURL();
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(8000); // 8 second timeout (improved)
            conn.setReadTimeout(8000); // 8 second read timeout
            
            // 添加User-Agent头避免被某些API阻止
            conn.setRequestProperty("User-Agent", "WeatherApp/1.0");
            
            int responseCode = conn.getResponseCode();
            
            if (responseCode == 200) {
                Scanner sc = new Scanner(conn.getInputStream());
                while (sc.hasNext()) {
                    inline.append(sc.nextLine());
                }
                sc.close();
            } else {
                // Handle different HTTP error codes with friendly messages
                String errorMessage = getErrorMessage(responseCode, location.getCity());
                serviceAvailable = false; // 标记服务不可用
                throw new RuntimeException(errorMessage);
            }
            
            conn.disconnect();
            
        } catch (java.io.FileNotFoundException e) {
            serviceAvailable = false;
            throw new RuntimeException("City '" + location.getCity() + "' not found. Please check the spelling and try again.");
        } catch (java.net.SocketTimeoutException e) {
            serviceAvailable = false;
            throw new RuntimeException("Request timeout. Please check your internet connection and try again.");
        } catch (java.net.UnknownHostException e) {
            serviceAvailable = false;
            throw new RuntimeException("Unable to connect to weather service. Please check your internet connection.");
        } catch (java.io.IOException e) {
            serviceAvailable = false;
            throw new RuntimeException("Network error: " + e.getMessage());
        } catch (Exception e) {
            if (e instanceof RuntimeException) {
                throw e; // Re-throw our custom error messages
            }
            serviceAvailable = false;
            throw new RuntimeException("Failed to fetch weather data: " + e.getMessage());
        }
        
        System.out.println("API Response: " + inline.toString()); // Debug log
        return inline.toString();
    }

    private WeatherData parseAPIResponse(String response) {
        try {
            if (response == null || response.trim().isEmpty()) {
                throw new RuntimeException("Empty response from weather service.");
            }
            
            JsonObject jsonObj = JsonParser.parseString(response).getAsJsonObject();
            
            // Check if the response contains an error
            if (jsonObj.has("cod")) {
                String cod = jsonObj.get("cod").getAsString();
                if (!"200".equals(cod)) {
                    String message = jsonObj.has("message") ? 
                        jsonObj.get("message").getAsString() : 
                        "Unknown error from weather service";
                    throw new RuntimeException("Weather service error: " + message);
                }
            }
            
            double temp = jsonObj.getAsJsonObject("main").get("temp").getAsDouble();
            double humidity = jsonObj.getAsJsonObject("main").get("humidity").getAsDouble();
            double windSpeed = jsonObj.getAsJsonObject("wind").get("speed").getAsDouble();
            String description = jsonObj.getAsJsonArray("weather").get(0).getAsJsonObject().get("description").getAsString();

            System.out.println("Parsed temperature: " + temp + "°C"); // Debug log
            
            // Ensure temperature is in Celsius (metric units should already provide this)
            // If for some reason we receive Fahrenheit, convert it
            if (temp > 50) { // Likely Fahrenheit if over 50 (rare for Celsius weather)
                temp = fahrenheitToCelsius(temp);
                System.out.println("Converted temperature: " + temp + "°C"); // Debug log
            }

            // Passing userPreferences to WeatherData with confirmed metric units
            WeatherData data=new WeatherData(temp, humidity, windSpeed, description, "Current", userPreferences);
            saveWeatherToFile(data);
            return data;
            
        } catch (Exception e) {
            if (e instanceof RuntimeException) {
                throw e; // Re-throw our custom error messages
            }
            throw new RuntimeException("Failed to parse weather data: " + e.getMessage());
        }
    }

    private WeatherData[] parseForecastResponse(String response, int days) {
        try {
            JsonObject jsonObj = JsonParser.parseString(response).getAsJsonObject();
            JsonArray list = jsonObj.getAsJsonArray("list");
            
            // 限制预报天数
            int actualDays = Math.min(days, list.size());
            WeatherData[] forecastData = new WeatherData[actualDays];

            for (int i = 0; i < actualDays; i++) {
                JsonObject dayData = list.get(i).getAsJsonObject();
                double temp = dayData.getAsJsonObject("main").get("temp").getAsDouble();
                double humidity = dayData.getAsJsonObject("main").get("humidity").getAsDouble();
                double windSpeed = dayData.getAsJsonObject("wind").get("speed").getAsDouble();
                String description = dayData.getAsJsonArray("weather").get(0).getAsJsonObject().get("description").getAsString();

                // Ensure temperature is in Celsius
                if (temp > 50) { // Likely Fahrenheit if over 50
                    temp = fahrenheitToCelsius(temp);
                }

                // 提取时间信息用于预报日期
                String dateTime = "Forecast Day " + (i + 1);
                if (dayData.has("dt_txt")) {
                    dateTime = dayData.get("dt_txt").getAsString();
                }

                // Passing userPreferences to WeatherData with confirmed metric units
                forecastData[i] = new WeatherData(temp, humidity, windSpeed, description, dateTime, userPreferences);
            }

            return forecastData;
            
        } catch (Exception e) {
            throw new RuntimeException("Failed to parse forecast data: " + e.getMessage());
        }
    }
    
    // Temperature conversion utility method
    private double fahrenheitToCelsius(double fahrenheit) {
        return (fahrenheit - 32) * 5.0 / 9.0;
    }

    private String getErrorMessage(int responseCode, String cityName) {
        switch (responseCode) {
            case 401:
                return "API authentication failed. Please check the API key configuration.";
            case 404:
                return "City '" + cityName + "' not found. Please check the spelling and try again.";
            case 429:
                return "Too many requests. Please wait a moment and try again.";
            case 500:
                return "Weather service is temporarily unavailable. Please try again later.";
            case 503:
                return "Weather service is under maintenance. Please try again later.";
            default:
                return "Failed to fetch weather data. HTTP Error: " + responseCode;
        }
    }

    // 提供兼容性方法，保持现有代码工作
    public WeatherData[] getForecast(Location location) {
        // 使用用户配置的预报天数替代硬编码的5天
        return getForecast(location, userPreferences.getForecastDays());
    }

    /**
     * 获取API密钥
     */
    private String getApiKey() {
        String key = userPreferences.getOpenWeatherMapApiKey();
        return (key != null) ? key.trim() : "";
    }
}
